/*
 * Copyright � 2008 Nokia Corporation.
 */


#include "SudokuGameLogic.h"
#include <e32math.h>
#include <aknnotewrappers.h>


// ----------------------------------------------------------------------------
// CGrid
// ----------------------------------------------------------------------------
CGrid::CGrid ( )
    {
    }

CGrid::~CGrid ( )
    {
    iGridItems.Reset();
    iGridItems.Close();
    }

CGrid* CGrid::NewLC ( )
    {
    CGrid* self = new (ELeave)CGrid();
    CleanupStack::PushL (self );
    self->ConstructL ( );
    return self;
    }

CGrid* CGrid::NewL ( )
    {
    CGrid* self=CGrid::NewLC ( );
    CleanupStack::Pop ( ); // self;
    return self;
    }

void CGrid::ConstructL ( )
    {

    }

TBool CGrid::SetValue(TGridItem& aItem, TInt aValue, TBool& aConflict)
    {
    TBool right = ETrue;

    aConflict = EFalse;
    
    // Check validity
    if (aValue!=0)
        {
        for (TInt i=0 ; i<iGridItems.Count() ; i++)
            {
            if (iGridItems[i].iRow == aItem.iRow ||
                iGridItems[i].iCol == aItem.iCol ||
                iGridItems[i].iArea == aItem.iArea)
                {
                if (iGridItems[i].iValue == aValue)
                    {
                    // Mark this item to conflict with the new value
                    iGridItems[i].iShowConflict = ETrue;
                    aConflict = ETrue;
                    right = EFalse;
                    //break; // Break in comment because must go all grid items throught
                    }
                }
            }
        if (right)
            {
            aItem.iValue = aValue;
            }
        }
    else
        {
        right = EFalse;
        aItem.iValue = aValue;
        }
    
    return right;
    }

void CGrid::CleanConflicts()
    {
    for (TInt i=0 ; i<iGridItems.Count() ; i++)
        {
        iGridItems[i].iShowConflict = EFalse;
        }
    }


void CGrid::AddItem(TGridItem& aNewItem)
    {
    iGridItems.Append(aNewItem);
    }

void CGrid::CleanItemValue(TInt aIndex)
    {
    TGridItem& item = iGridItems[aIndex];
    item.iValue = 0;
    item.iShowConflict = EFalse;
    item.iLocked = EFalse; // User can change this item
    }

TInt CGrid::GetItem(TPoint& aPosition, TGridItem*& aItem)
    {
    // Finds grid item that has been pointed
    TInt err = KErrNotFound;
    for (TInt i=0 ; i<iGridItems.Count() ; i++)
        {
        if (iGridItems[i].iRect.Contains(aPosition))
            {
            // NOTE: aItem is pointer reference
            aItem = &iGridItems[i];
            err = KErrNone;
            break;
            }
        }
    return err;
    }

void CGrid::SetItemRects(TRect& aTl)
    {
    // Calculate TRect value to all grid items
    TRect tmp = aTl;
    for (TInt i=0 ; i<iGridItems.Count() ; i++)
        {
        // NOTE: take TGridItem reference
        TGridItem& item = iGridItems[i];

        if (item.iCol != 0 && item.iRow != 0)
            {
            aTl.Move(aTl.Width()*(item.iCol),
                     aTl.Height()*(item.iRow));
            }
        else if(item.iCol == 0 && item.iRow != 0)
            {
            aTl.Move(0,
                     aTl.Height()*(item.iRow));
            
            }
        else if(item.iCol != 0 && item.iRow == 0)
            {
            aTl.Move(aTl.Width()*(item.iCol),
                     0);
            }
        
        item.iRect = aTl;
        aTl = tmp;
        }
    }

RArray<TGridItem>* CGrid::Items()
    {
    return &iGridItems;
    }



// ----------------------------------------------------------------------------
// CSudokuGameLogic
// ----------------------------------------------------------------------------
CSudokuGameLogic::CSudokuGameLogic ( )
    {
    }

CSudokuGameLogic::~CSudokuGameLogic ( )
    {
    iFile.Close();
    iRfs.Close();
    delete iSudokuGrid;
    delete iSudokuData;
    }

CSudokuGameLogic* CSudokuGameLogic::NewLC ( )
    {
    CSudokuGameLogic* self = new (ELeave)CSudokuGameLogic();
    CleanupStack::PushL (self );
    self->ConstructL ( );
    return self;
    }

CSudokuGameLogic* CSudokuGameLogic::NewL ( )
    {
    CSudokuGameLogic* self=CSudokuGameLogic::NewLC ( );
    CleanupStack::Pop ( ); // self;
    return self;
    }

void CSudokuGameLogic::ConstructL ( )
    {
    iTime.HomeTime();
    iRandSeed = iTime.Int64();

    iRfs.Connect();
    
    NewGameL();
    }

void CSudokuGameLogic::NewGameL()
    {
    LoadBoardL();
    CreateBoardL();
    RandomRemove();
    iMoves = 0;
    CalculateEmptyCount();
    }

void CSudokuGameLogic::SetValue(TPoint& aPosition, TInt aValue)
    {
    TBool ret = EFalse;
    if (iSudokuGrid)
        {
        // Find active grid item
        TGridItem* item = NULL;
        TInt ret = iSudokuGrid->GetItem(aPosition, item);
        TInt oldValue = item->iValue;
        if (ret==KErrNone)
            {
            ret = iSudokuGrid->SetValue(*item, aValue,iConfictValues);
            // If accepted value and that was first
            // value into item, descreace empty count -1
            if (ret && oldValue == 0)
                {
                iEmpty--;
                }
            // If user clean prior accepted value
            // increase empty count +1
            else if (aValue == 0 && oldValue != 0)
                {
                iEmpty++;
                }
            }
        }

    // Moves is amount of inserted values by user
    iMoves++;
    }


TInt CSudokuGameLogic::RandInt(const TInt aLow, const TInt aHigh)
    {
    return (Math::Rand(iRandSeed) % ((aHigh + 1) - aLow) + aLow);
    }

void CSudokuGameLogic::LoadBoardL()
    {
    // Loading ramdomed board from c:\private\E4824C7F\boards -folder
    TFileName path;
    iRfs.PrivatePath(path);
    path.Append(_L("boards\\"));

    // Read board filder files count
    CDir* fileList = NULL;
    iRfs.GetDir(path,KEntryAttNormal,ESortByName,fileList);
    CleanupStack::PushL(fileList);

    // Random used grid index
    TInt randGrid = RandInt(0,fileList->Count()-1); 
    path.Append(fileList->operator[](randGrid).iName);

    
    // Load Sudoku grid
    iFile.Close();
    User::LeaveIfError(iFile.Open(iRfs,path,EFileRead));
    TInt size = 0;
    iFile.Size(size);

    // Read buffer 8bit
    HBufC8* dataBuf = HBufC8::NewLC(size);    
    TPtr8 dataPtr = dataBuf->Des();

    // Read file loop
    TInt err = iFile.Read(0,iReadBuffer8);
    while(err == KErrNone && iReadBuffer8.Length() > 0)
        {
        dataPtr.Append(iReadBuffer8);
        iFile.Read(iReadBuffer8);
        }

    // Convert to 16 bit
    delete iSudokuData;
    iSudokuData = NULL;
    iSudokuData = HBufC16::NewL(size);
    iSudokuData->Des().Copy(dataPtr);
    
    CleanupStack::PopAndDestroy(dataBuf);
    CleanupStack::PopAndDestroy(fileList);
    }

void CSudokuGameLogic::CreateBoardL()
    {
    // Create new Sudoku grid
    delete iSudokuGrid;
    iSudokuGrid = NULL;
    iSudokuGrid = CGrid::NewL();    
    
    if (!iSudokuData)
        {
        User::Leave(KErrNotReady);
        }

    // Parse loaded Sudoku game grid
    TLex16    lex(*iSudokuData);
    TLex16    lex2;

    // Create grid item
    TInt    number = 0;
    TPtrC   value;
    TInt    row = 0;
    TInt    col = 0;
    TInt    arearow = 0;
    TInt    areacol = 0;
    TInt    area = 0;
    TInt count = SUDOKU_GRID_SIZE*SUDOKU_GRID_SIZE;
    for (TInt i=0 ; i<count ; i++)
        {
        // Parse string number
        value.Set(lex.NextToken());
        lex.Inc(); // Move to next, there is spaces between numbers

        // String to int
        lex2 = value;
        User::LeaveIfError(lex2.Val(number));

        // Calculate item row, col, area
        row = i / SUDOKU_GRID_SIZE;
        col = i % SUDOKU_GRID_SIZE;
        arearow = row / SUDOKU_AREA_SIZE;
        areacol = col / SUDOKU_AREA_SIZE;
        area = arearow * SUDOKU_AREA_SIZE + areacol;

        TGridItem item;
        item.iValue = number;
        item.iRow = row;
        item.iCol = col;
        item.iArea = area;
        item.iRect = TRect(0,0,0,0); // These will be defined in SetBoardItemRects()
        item.iShowConflict = EFalse;
        item.iLocked = ETrue;
        iSudokuGrid->AddItem(item);
        }
    }

void CSudokuGameLogic::SetBoardItemRects(TRect aBoardRect)
    {
    if (iSudokuGrid)
        {
        // Calculate Sudoku board grid item rects
        TInt itemWidth = aBoardRect.Width() / SUDOKU_GRID_SIZE;
        TInt itemHeight = aBoardRect.Height() / SUDOKU_GRID_SIZE;

        // Tune TL point
        TInt offsetX = (aBoardRect.Width() - itemWidth * SUDOKU_GRID_SIZE) /2;
        TInt offsetY = (aBoardRect.Height() - itemHeight * SUDOKU_GRID_SIZE) /2;
        aBoardRect.Move(offsetX,offsetY);
        
        TRect itemRect(aBoardRect.iTl,TSize(itemWidth,itemHeight));
        iSudokuGrid->SetItemRects(itemRect);
        }
    }

RArray<TGridItem>* CSudokuGameLogic::GridItems()
    {
    if (iSudokuGrid)
        {
        return iSudokuGrid->Items();
        }
    return NULL;
    }

void CSudokuGameLogic::RandomRemove()
    {
    // Remove half of the numbers out from full Sudoku grid
    TInt half = iSudokuGrid->Items()->Count() /2;
    for (TInt i=0 ; i<half ; i++)
        {
        TInt randomIndex = RandInt(0,iSudokuGrid->Items()->Count()-1);
        iSudokuGrid->CleanItemValue(randomIndex);
        }
    }

TGridItem* CSudokuGameLogic::GetActiveItem(TPoint aPoint)
    {
    TGridItem* item = NULL;
    iSudokuGrid->GetItem(aPoint, item);
    return item;
    }

void CSudokuGameLogic::CleanConflicts()
    {
    iSudokuGrid->CleanConflicts();
    iConfictValues = EFalse;
    }

void CSudokuGameLogic::CalculateEmptyCount()
    {
    iEmpty = 0;
    TInt count = iSudokuGrid->Items()->Count();
    for (TInt i=0 ; i<count ; i++)
        {
        TGridItem* item = &(*iSudokuGrid->Items())[i];
        if (item->iValue == 0)
            {
            iEmpty++;
            }
        }
    }

void CSudokuGameLogic::RestartGame()
    {
    iMoves = 0;
    CalculateEmptyCount();
    TInt count = iSudokuGrid->Items()->Count();
    for (TInt i=0 ; i<count ; i++)
        {
        TGridItem* item = &(*iSudokuGrid->Items())[i];
        if (!item->iLocked)
            {
            item->iValue = 0;
            }
        item->iShowConflict = EFalse;
        }
    }


